/*============================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.

Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.

============================================================================*/

#include "mitkPropertyListsXmlFileReaderAndWriter.h"
#include "mitkProperties.h"
#include "mitkStandaloneDataStorage.h"
#include <itksys/SystemTools.hxx>
#include <tinyxml.h>

namespace mitk
{
  bool PropertyListsXmlFileReaderAndWriter::PropertyFromXmlElem(std::string &name,
                                                                mitk::BaseProperty::Pointer &prop,
                                                                TiXmlElement *elem) const
  {
    if (!elem)
    {
      return false;
    }

    bool readOp = false;
    std::string type = "";
    readOp = elem->QueryStringAttribute("type", &type) == TIXML_SUCCESS;
    if (readOp)
      readOp = elem->QueryStringAttribute("name", &name) == TIXML_SUCCESS;
    else
      MITK_WARN << "type"
                << " attribute not found in a property";

    if (readOp)
    {
      if (type == "BoolProperty")
      {
        int val = 0;
        readOp = elem->QueryIntAttribute("value", &val) == TIXML_SUCCESS;
        if (readOp)
        {
          prop = mitk::BoolProperty::New(val == 1 ? true : false);
        }
      }
      else if (type == "StringProperty")
      {
        std::string val = "";
        readOp = elem->QueryStringAttribute("value", &val) == TIXML_SUCCESS;
        if (readOp)
        {
          prop = mitk::StringProperty::New(val);
        }
      }
      else if (type == "IntProperty")
      {
        int val = 0;
        readOp = elem->QueryIntAttribute("value", &val) == TIXML_SUCCESS;
        if (readOp)
        {
          prop = mitk::IntProperty::New(val);
        }
      }
      else if (type == "DoubleProperty")
      {
        double val = 0;
        readOp = elem->QueryDoubleAttribute("value", &val) == TIXML_SUCCESS;
        if (readOp)
        {
          prop = mitk::DoubleProperty::New(val);
        }
      }
      else if (type == "FloatProperty")
      {
        float val = 0;
        readOp = elem->QueryFloatAttribute("value", &val) == TIXML_SUCCESS;
        if (readOp)
        {
          prop = mitk::FloatProperty::New(val);
        }
      }
      else
      {
        readOp = false;
        MITK_WARN << "type"
                  << " attribute unknown. Only BoolProperty, StringProperty, IntProperty, DoubleProperty or "
                     "FloatProperty allowed.";
      }
    }
    else
      MITK_WARN << "name"
                << " attribute not found in a property";

    if (!readOp)
      MITK_WARN << "value"
                << " attribute not found in a property";

    return readOp;
  }
  bool PropertyListsXmlFileReaderAndWriter::PropertyToXmlElem(const std::string &name,
                                                              const mitk::BaseProperty *prop,
                                                              TiXmlElement *elem) const
  {
    if (!prop || !elem)
    {
      return false;
    }

    const mitk::IntProperty *intProp = nullptr;
    const mitk::FloatProperty *floatProp = nullptr;
    const mitk::DoubleProperty *doubleProp = nullptr;
    const mitk::BoolProperty *boolProp = nullptr;
    const mitk::StringProperty *stringProp = nullptr;
    bool writeOp = true;

    if ((boolProp = dynamic_cast<const BoolProperty *>(prop)))
    {
      elem->SetAttribute(GetPropertyListIdElementName(), name);
      elem->SetAttribute("value", boolProp->GetValue() ? 1 : 0);
      elem->SetAttribute("type", "BoolProperty");
    }
    else if ((stringProp = dynamic_cast<const StringProperty *>(prop)))
    {
      elem->SetAttribute(GetPropertyListIdElementName(), name);
      elem->SetAttribute("value", stringProp->GetValue());
      elem->SetAttribute("type", "StringProperty");
    }
    else if ((intProp = dynamic_cast<const IntProperty *>(prop)))
    {
      elem->SetAttribute(GetPropertyListIdElementName(), name);
      elem->SetAttribute("value", intProp->GetValue());
      elem->SetAttribute("type", "IntProperty");
    }
    else if ((doubleProp = dynamic_cast<const DoubleProperty *>(prop)))
    {
      elem->SetAttribute(GetPropertyListIdElementName(), name);
      elem->SetDoubleAttribute("value", doubleProp->GetValue());
      elem->SetAttribute("type", "DoubleProperty");
    }
    else if ((floatProp = dynamic_cast<const FloatProperty *>(prop)))
    {
      elem->SetAttribute(GetPropertyListIdElementName(), name);
      elem->SetDoubleAttribute("value", static_cast<float>(floatProp->GetValue()));
      elem->SetAttribute("type", "FloatProperty");
    }
    else
    {
      MITK_WARN("PropertyListImportFromXmlFile") << "Base property " << name << " is unknown";
      writeOp = false;
    }
    return writeOp;
  }
  bool PropertyListsXmlFileReaderAndWriter::WriteLists(
    const std::string &fileName, const std::map<std::string, mitk::PropertyList::Pointer> &_PropertyLists) const
  {
    TiXmlDocument doc;

    auto decl = new TiXmlDeclaration("1.0", "", "");
    doc.LinkEndChild(decl);
    // create root
    auto propertyListsElem = new TiXmlElement("PropertyLists");

    bool allPropsConverted = true;
    auto it = _PropertyLists.begin();
    while (it != _PropertyLists.end())
    {
      const std::string &id = (*it).first;
      const PropertyList *propList = (*it).second;
      auto propertyListElem = new TiXmlElement("PropertyList");
      propertyListElem->SetAttribute(GetPropertyListIdElementName(), id);

      const std::map<std::string, BaseProperty::Pointer> *propMap = propList->GetMap();
      auto propMapIt = propMap->begin();
      while (propMapIt != propMap->end())
      {
        const std::string &propName = (*propMapIt).first;
        const BaseProperty *prop = (*propMapIt).second;
        auto propertyElem = new TiXmlElement("Property");

        if (!this->PropertyToXmlElem(propName, prop, propertyElem))
          allPropsConverted = false;

        propertyListElem->LinkEndChild(propertyElem);
        ++propMapIt;
      }

      propertyListsElem->LinkEndChild(propertyListElem);
      ++it;
    }

    doc.LinkEndChild(propertyListsElem);

    return (allPropsConverted && doc.SaveFile(fileName.c_str()));
  }
  bool PropertyListsXmlFileReaderAndWriter::ReadLists(
    const std::string &fileName, std::map<std::string, mitk::PropertyList::Pointer> &_PropertyLists) const
  {
    // reread
    TiXmlDocument doc(fileName);
    doc.LoadFile();

    TiXmlHandle docHandle(&doc);
    TiXmlElement *elem = docHandle.FirstChildElement("PropertyLists").FirstChildElement("PropertyList").ToElement();

    if (!elem)
    {
      MITK_WARN("PropertyListFromXml") << "Cannot find a PropertyList element (inside a PropertyLists element)";
      return false;
    }

    bool opRead = false;
    while (elem)
    {
      std::string propListId;
      opRead = elem->QueryStringAttribute(GetPropertyListIdElementName(), &propListId) == TIXML_SUCCESS;
      if (!opRead)
        break;

      mitk::PropertyList::Pointer propList = mitk::PropertyList::New();

      TiXmlElement *propElem = elem->FirstChildElement("Property");

      while (propElem)
      {
        std::string name;
        mitk::BaseProperty::Pointer prop;
        opRead = this->PropertyFromXmlElem(name, prop, propElem);
        if (!opRead)
          break;
        propList->SetProperty(name, prop);
        propElem = propElem->NextSiblingElement("Property");
      }

      if (!opRead)
        break;
      _PropertyLists[propListId] = propList;
      elem = elem->NextSiblingElement("PropertyList");
    }

    return opRead;
  }

  PropertyListsXmlFileReaderAndWriter::PropertyListsXmlFileReaderAndWriter() {}
  PropertyListsXmlFileReaderAndWriter::~PropertyListsXmlFileReaderAndWriter() {}
  const char *PropertyListsXmlFileReaderAndWriter::GetPropertyListIdElementName() { return "name"; }
}
